package com.enjoyor.soa.traffic.frame.interceptor;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import java.util.Properties;

import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.log4j.Logger;

import com.alibaba.fastjson.JSONObject;

@Intercepts(value = {
        @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }),
        @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
                RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class }),
        @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
                RowBounds.class, ResultHandler.class }) })
public class SqlInterceptor implements Interceptor {

    private static final Logger logger = Logger.getLogger(SqlInterceptor.class);

    private String showSql;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        Object target = invocation.getTarget();
        Object result = null;

        result = invocation.proceed();
        
        if (Boolean.valueOf(showSql) && target instanceof Executor) {

            long start = System.currentTimeMillis();
            Method method = invocation.getMethod();           
            long end = System.currentTimeMillis();
            final Object[] args = invocation.getArgs();
            MappedStatement mappedStatement = (MappedStatement) args[0];
            String SqlStr = printSql(method, mappedStatement, start, end, result, args);
            logger.info(SqlStr);

        }
        return result;
    }

    @SuppressWarnings("rawtypes")
    public String printSql(Method method, MappedStatement mappedStatement, long start, long end, Object result,
            Object... args) {

        String commandName = mappedStatement.getSqlCommandType().name();
        String name = method.getName();
        if (commandName.startsWith("INSERT")) {
            name += "=新增";
        } else if (commandName.startsWith("UPDATE")) {
            name += "=修改";
        } else if (commandName.startsWith("DELETE")) {
            name += "=删除";
        } else if (commandName.startsWith("SELECT")) {
            name += "=查询";
        }
        String message = String.format("[SqlInterceptor] execute [{%s}] cost [{%s}] ms",
                new Object[] { name, (end - start) });
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append(message + System.getProperty("line.separator"));
        Object parameterObject = args[1];
        BoundSql boundSql = mappedStatement.getBoundSql(parameterObject);
        String sql = boundSql.getSql();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        String parameterObjects = JSONObject.toJSONString(boundSql.getParameterObject());

        stringBuffer
                .append(String.format("id:{%s}\\r\\n sql:{%s}\\n parameterMappings:{%s} \\n parameterObjects:{%s} \\n",
                        new Object[] { mappedStatement.getId(), sql, parameterMappings, parameterObjects }));
        if (result != null) {
            if (result instanceof List) {
                stringBuffer.append("result=" + ((List) result).size());
            } else if (result instanceof Collection) {
                stringBuffer.append("result=" + ((Collection) result).size());
            } else {
                stringBuffer.append("result=" + 1);
            }
        } else {
            stringBuffer.append("result=NULL");
        }
        stringBuffer.append("\n");
        return stringBuffer.toString();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        this.showSql = properties.getProperty("showSql");
    }

}
